uniform float AlphaScale;
uniform float ColorScale;
uniform float ValueScale;
uniform float ValueShift;
uniform float SampleStep;
uniform float Ka;
uniform float Kd;
uniform float Ks;
uniform float Kp;
uniform vec3 LhtDir;
uniform vec3 BoxMin;
uniform vec3 BoxMax;
uniform vec3 VoxelSize;
uniform mat4 Projection;
uniform sampler3D Volume;
uniform sampler2D Transfer;

varying vec3 RayOri;
varying vec3 RayDir;
varying mat3 RayRot;

struct Ray
{
	vec3 ori;   // origin
	vec3 dir;   // direction
};

bool IntersectBox(Ray ray, vec3 boxMin, vec3 boxMax, out float t0, out float t1)
{
	vec3 invDir = 1.0 / ray.dir;
	vec3 tBot = invDir * (boxMin.xyz - ray.ori);
	vec3 tTop = invDir * (boxMax.xyz - ray.ori);

	vec3 tMin = min(tTop, tBot);
	vec3 tMax = max(tTop, tBot);

	vec2 temp = max(tMin.xx, tMin.yz);
	float tMinMax = max(temp.x, temp.y);
	temp = min(tMax.xx, tMax.yz);
	float tMaxMin = min(temp.x, temp.y);

	bool hit;
	if((tMinMax > tMaxMin)) 
		hit = false;
	else
		hit = true;

	t0 = tMinMax;
	t1 = tMaxMin;

	return hit;
}

vec3 SampleNormal(vec3 x0)
{
	vec3 normal;
	normal.x = texture3D(Volume, x0 + vec3(VoxelSize.x, 0, 0)).r - texture3D(Volume, x0 - vec3(VoxelSize.x, 0, 0)).r;
	normal.y = texture3D(Volume, x0 + vec3(0, VoxelSize.y, 0)).r - texture3D(Volume, x0 - vec3(0, VoxelSize.y, 0)).r;
	normal.z = texture3D(Volume, x0 + vec3(0, 0, VoxelSize.z)).r - texture3D(Volume, x0 - vec3(0, 0, VoxelSize.z)).r;
	if(dot(normal, normal) > 0.0)
		normal = normalize(normal);
	return normal;
}

void main(void)
{
	Ray ray;
	ray.ori = RayOri;
	ray.dir = RayDir;
	float t0, t1;
	bool hit = IntersectBox(ray, BoxMin, BoxMax, t0, t1);
	if(!hit || t1 < 0.0)
		discard;
	if(t0 < 0.0)
		t0 = 0.0;

	float ti = t0;
	vec3 invBoxDim = 1.0 / (BoxMax - BoxMin);
	vec4 final = vec4(0.0, 0.0, 0.0, 0.0);
	hit = false;

	int steps = int((t1 - t0) / SampleStep) + 1;
	for(int i = 0; i < steps; ++i)
	{
		vec3 pos = ray.ori + ray.dir * ti;
		pos = (pos - BoxMin) * invBoxDim;
		float val = (texture3D(Volume, pos).r + ValueShift) * ValueScale;
		vec4 col = texture2D(Transfer, vec2(val, 0.5));
		col.a *= AlphaScale;
		if(col.a >= 0.001)
		{
			if(!hit)
			{
				vec4 proj = Projection * vec4(ray.dir * ti * RayRot, 1.0);
				gl_FragDepth = clamp(proj.z / proj.w * 0.5 + 0.5, 0.0, 1.0);
				hit = true;
			}

			// lighting
			vec3 n = SampleNormal(pos);
			col.rgb *= Ka + Kd * max(0.0, dot(n, LhtDir));
			col.rgb += Ks * pow(max(0.0, dot(n, (LhtDir + RayDir) * 0.5)), Kp);
			col.rgb *= col.a;

			// blending
			col *= (1.0 - final.a);
			final += col;
			if(final.a > 0.999)
				break;
		}
		ti += SampleStep;
		if(ti > t1)
			break;
	}

	if(hit)
		gl_FragColor = vec4(final.rgb * ColorScale, final.a);
	else
		discard;
}
